﻿using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Xml;
using EPiServer.Core;
using TemplateFoundation.Core;

namespace TemplateFoundation.Handlers.JavaScriptTranslate
{
    /// <summary>
    /// Used to create a JSON language object based on an EPiServer language file
    /// </summary>
    /// <remarks>Does not support nodes having both a value AND child nodes (like the LanguageManager.Translate() method does for templates)</remarks>
    public class LanguageScriptBuilder
    {
        private LanguageManager _languageManager;

        /// <summary>
        /// Initializes a script builder based on an EPiServer LanguageManager
        /// </summary>
        /// <param name="languageManager"></param>
        public LanguageScriptBuilder(LanguageManager languageManager)
        {
            _languageManager = languageManager;
        }

        #region Script generator

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <param name="isFirstNode">Determines whether or not the value pair should be preceded by a comma</param>
        /// <returns></returns>
        private static string GetScriptStringForSingleKeyValuePair(string name, string value, bool isFirstNode)
        {
            return string.Format("{0}'{1}' : '{2}' \n", isFirstNode ? "" : ",\n", name, value);
        }

        private static string GetOpeningScriptStringForNodeWithChildren(string name, bool isFirstNode)
        {
            return string.Format("{0} '{1}' : \n{{", isFirstNode ? "" : "},\n", name);
        }

        private static string GetClosingScriptStringForNodeWithChildren()
        {
            return "}\n}";
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        /// <param name="isFirstNode">Determines whether or not the value pair should be preceded by a comma</param>
        /// <param name="isLastNode">Determines whether or not the value pair should be closed by curly braces</param>
        /// <returns></returns>
        private static string GenerateScriptForNode(LanguageNode node, bool isFirstNode, bool isLastNode)
        {
            var sb = new StringBuilder();

            var name = node.Name.Replace("\r", "").Replace("\r", "").Trim();

            var value = node.Value != null ? node.Value.Replace("\r", "").Replace("\r", "").Trim() : string.Empty;

            var children = node.Children;

            if (children.Any())
            {
                sb.Append(GetOpeningScriptStringForNodeWithChildren(name, isFirstNode));

                var childrenAdded = 0;

                foreach (var childNode in children)
                {
                    sb.Append(GenerateScriptForNode(childNode, childrenAdded==0, childrenAdded==children.Count-1));

                    childrenAdded++;
                }

                if (isLastNode)
                {
                    sb.Append(GetClosingScriptStringForNodeWithChildren());
                }
            }
            else
            {
                sb.Append(GetScriptStringForSingleKeyValuePair(name, value, isFirstNode));
            }

            return sb.ToString();
        }

        #endregion

        public string GetScript(string languageBranch)
        {
            Debug.WriteLine("[ETF] Generating language object for script file");

            var sb = new StringBuilder();

            // Script 'header'
            sb.Append(
                @"
                    var LANG = {};

                    LANG.Init = function () {
	                    this.translations =
                ");

            if (!_languageManager.LanguageDocument.Root.Children.Any(a => a.Name == languageBranch))
            {
                languageBranch = PageReference.StartPage.GetPage().MasterLanguageBranch;
            }

            var rootNode = _languageManager.LanguageDocument.Root.Children.FirstOrDefault(n => n.Name == languageBranch);

            if (rootNode==null || rootNode.Value == null || !rootNode.Children.Any())
            {
                return string.Empty;
            }

            var xml = GetInnerXml(rootNode, false);

            if (string.IsNullOrWhiteSpace(xml))
            {
                return string.Empty;
            }

            var xmlDoc = new XmlDocument();
            
            xmlDoc.LoadXml(xml);

            var json = XmlToJsonConverter.Convert(xmlDoc).Replace(@"\", @"\\");

            // Replace Json from root node
            json = json.Replace(string.Format("{{ \"{0}\":", languageBranch), string.Empty);
            json = json.Substring(0, json.Length - 1);

            sb.Append(json);

            // Script 'footer'
            sb.Append(
                @"
                    };
                ");

            return sb.ToString();
        }

        private string GetInnerXml(LanguageNode rootNode, bool skipRootNode)
        {
            string xml = null;

            var nodes = new List<LanguageNode>();

            if (!skipRootNode)
            {
                nodes.Add(rootNode);
            }
            else
            {
                nodes.AddRange(rootNode.Children);
            }

            foreach (var node in nodes)
            {
                if (node.Children.Any())
                {
                    var nodeInnerXml = node.Children.Aggregate("", (current, child) => current + GetInnerXml(child, false));

                    xml += string.Format("<{0}>{1}</{0}>", node.Name, nodeInnerXml);
                }
                else
                {
                    xml = string.Format("<{0}>{1}</{0}>", node.Name, node.Value);                        
                }
            }

            return xml;
        }
    }
}

/* 
=== REFERENCE ===

This is the type of script we want to inject at the top of the original script file:

var LANG = {};

LANG.Init = function () {
	this.translations = {
		'forms': {
			'formGenericValidationErrorMessage': 'This is a required field',
			'formEmailValidationErrorMessage': 'Email address has bad format'
		},
		'globalTextLabels': {
			'close': 'Close',
			'shareThis': 'Share this'
		},
		'globalMessages': {
			'thisIsMe': 'Hi man!',
            'secondLevel':
            {
                'thirdLevel': 'Tredje nivån',
            }
		}
	}
};

*/